import {Point} from '../../type';
import {PlotTypes} from '../../enum/PlotTypes';
import * as Constants from '../../constants';
import * as PlotUtils from '../../utils';
import {
    calculateNewCoordinateOfRotate,
    calculateNewCoordinateOfScale,
    calculateNewCoordinateOfTranslate
} from '../../utils';

// @ts-ignore
export default class DoubleArrow extends ol.geom.Polygon {
    public params: any;
    public isPlot: boolean = true;
    public fixPointCount: number = 4;
    public headHeightFactor: number = 0.25;
    public headWidthFactor: number = 0.3;
    public neckHeightFactor: number = 0.85;
    public neckWidthFactor: number = 0.15;
    public connPoint: Point | null = null;
    public tempPoint4: Point | null = null;

    private readonly _type: PlotTypes;
    private points: Array<Point>;

    constructor(coordinates: any, points: Array<Point>, params: any) {
        super([]);
        this._type = PlotTypes.DOUBLE_ARROW;
        this.points = [];
        this.params = params;
        if (points && points.length > 0) {
            this.setPoints(points);
        } else if (coordinates && coordinates.length > 0) {
            // @ts-ignore
            this.setCoordinates(coordinates);
        }
    }

    public transform(type: string, params: any): any {
        if (type === 'scale') {
            const {scaleX, scaleY, centerX, centerY, paths} = params;
            const scale: number = Math.max(scaleX, scaleY);
            const newPoints = calculateNewCoordinateOfScale([scaleX, scaleY], [centerX, centerY], paths);
            this.setPoints(newPoints);
        } else if (type === 'rotate') {
            const {angle, centerX, centerY, paths} = params;
            const newPaths = calculateNewCoordinateOfRotate(paths, [centerX, centerY], angle);
            this.setPoints(newPaths);
        } else if (type === 'translate') {
            const {deltaX, deltaY, centerX, centerY, paths} = params;
            const newPoints = calculateNewCoordinateOfTranslate(paths, [deltaX, deltaY]);
            this.setPoints(newPoints);
        }
    }

    /**
     * 获取标绘类型
     * @return {PlotTypes}
     */
    public getPlotType(): PlotTypes {
        return this._type;
    }

    /**
     * 获取坐标点
     * @return {Array<Point>}
     */
    public getPoints(): Array<Point> {
        return this.points.slice(0);
    }

    /**
     * 获取点数理
     * @return {number}
     */
    public getPointCount(): number {
        return this.points.length;
    }

    /**
     * 更新当前坐标
     * @param point
     * @param index
     */
    public updatePoint(point: Point, index: number): void {
        if (index >= 0 && index < this.points.length) {
            this.points[index] = point;
            this.generate();
        }
    }

    private updateLastPoint(point: Point): void {
        this.updatePoint(point, this.points.length - 1);
    }

    /**
     * 完成绘制
     */
    public finishDrawing(): void {
        console.log('finishDrawing', this.getPointCount(), this.tempPoint4, this.connPoint);
        if (this.getPointCount() === 3 && this.tempPoint4 !== null) {
            this.points.push(this.tempPoint4);
        }
        if (this.connPoint !== null) {
            this.points.push(this.connPoint);
        }
    }

    private setPoints(value: Array<Point>): void {
        this.points = !value ? [] : value;
        if (this.points.length >= 1) {
            this.generate();
        }
    }

    private generate(): void {
        try {
            let count: number = this.getPointCount();
            if (count < 2) {
                return;
            } else if (count === 2) {
                // @ts-ignore
                this.setCoordinates([this.points]);
                return;
            }
            if (count > 2) {
                let [pnt1, pnt2, pnt3]: [pnt1: Point, pnt2: Point, pnt3: Point] = [this.points[0], this.points[1], this.points[2]];
                if (count === 3) {
                    this.tempPoint4 = this.getTempPoint4(pnt1, pnt2, pnt3) as Point;
                    this.connPoint = PlotUtils.Mid(pnt1, pnt2);
                } else if (count === 4) {
                    this.tempPoint4 = this.points[3];
                    this.connPoint = PlotUtils.Mid(pnt1, pnt2);
                } else {
                    this.tempPoint4 = this.points[3];
                    this.connPoint = this.points[4];
                }
                let leftArrowPoints: Array<Point>;
                let rightArrowPoints: Array<Point>;
                if (PlotUtils.isClockWise(pnt1, pnt2, pnt3)) {
                    leftArrowPoints = this.getArrowPoints(pnt1, this.connPoint, this.tempPoint4 as Point, false) as Array<Point>;
                    rightArrowPoints = this.getArrowPoints(this.connPoint, pnt2, pnt3, true) as Array<Point>;
                } else {
                    leftArrowPoints = this.getArrowPoints(pnt2, this.connPoint, pnt3, false) as Array<Point>;
                    rightArrowPoints = this.getArrowPoints(this.connPoint, pnt1, this.tempPoint4 as Point, true) as Array<Point>;
                }
                let m: number = leftArrowPoints.length;
                let t: number = (m - 5) / 2;
                let llBodyPoints: Array<Point> = leftArrowPoints.slice(0, t);
                let lArrowPoints: Array<Point> = leftArrowPoints.slice(t, t + 5);
                let lrBodyPoints: Array<Point> = leftArrowPoints.slice(t + 5, m);
                let rlBodyPoints: Array<Point> = rightArrowPoints.slice(0, t);
                let rArrowPoints: Array<Point> = rightArrowPoints.slice(t, t + 5);
                let rrBodyPoints: Array<Point> = rightArrowPoints.slice(t + 5, m);
                rlBodyPoints = PlotUtils.getBezierPoints(rlBodyPoints);
                let bodyPoints: Array<Point> = PlotUtils.getBezierPoints(rrBodyPoints.concat(llBodyPoints.slice(1)));
                lrBodyPoints = PlotUtils.getBezierPoints(lrBodyPoints);
                let points: Array<Point> = rlBodyPoints.concat(rArrowPoints, bodyPoints, lArrowPoints, lrBodyPoints);
                // @ts-ignore
                this.setCoordinates([points]);
            }
        } catch (e) {
            console.log(e);
        }
    }

    /**
     * 插值点数据
     * @param pnt1
     * @param pnt2
     * @param pnt3
     * @param clockWise
     * @private
     */
    private getArrowPoints(pnt1: Point, pnt2: Point, pnt3: Point, clockWise: boolean): Array<Point> | undefined {
        let midPnt: Point = PlotUtils.Mid(pnt1, pnt2);
        let len: number = PlotUtils.mathDistance(midPnt, pnt3);
        let midPnt1: Point = PlotUtils.getThirdPoint(pnt3, midPnt, 0, len * 0.3, true);
        let midPnt2: Point = PlotUtils.getThirdPoint(pnt3, midPnt, 0, len * 0.5, true);
        midPnt1 = PlotUtils.getThirdPoint(midPnt, midPnt1, Constants.HALF_PI, len / 5, clockWise);
        midPnt2 = PlotUtils.getThirdPoint(midPnt, midPnt2, Constants.HALF_PI, len / 4, clockWise);
        let points: Array<Point> = [midPnt, midPnt1, midPnt2, pnt3];
        let arrowPoints: Array<Point> = this.getArrowHeadPoints(points) as Array<Point>;
        if (arrowPoints && Array.isArray(arrowPoints) && arrowPoints.length > 0) {
            let [neckLeftPoint, neckRightPoint]: [neckLeftPoint: Point, neckRightPoint: Point] = [arrowPoints[0], arrowPoints[4]];
            let tailWidthFactor: number = PlotUtils.mathDistance(pnt1, pnt2) / PlotUtils.getBaseLength(points) / 2;
            let bodyPoints: Array<Point> = this.getArrowBodyPoints(points, neckLeftPoint, neckRightPoint, tailWidthFactor) as Array<Point>;
            if (bodyPoints) {
                let n: number = bodyPoints.length;
                let lPoints: Array<Point> = bodyPoints.slice(0, n / 2);
                let rPoints: Array<Point> = bodyPoints.slice(n / 2, n);
                lPoints.push(neckLeftPoint);
                rPoints.push(neckRightPoint);
                lPoints = lPoints.reverse();
                lPoints.push(pnt2);
                rPoints = rPoints.reverse();
                rPoints.push(pnt1);
                return (lPoints.reverse().concat(arrowPoints, rPoints));
            }
        } else {
            throw new Error('插值出错');
        }
    }

    /**
     * 插值头部点
     * @param points
     * @returns {[*,*,*,*,*]}
     */
    private getArrowHeadPoints(points: Array<Point>): Array<Point> | undefined {
        try {
            let len: number = PlotUtils.getBaseLength(points);
            let headHeight: number = len * this.headHeightFactor;
            let headPnt: Point = points[points.length - 1];
            let headWidth: number = headHeight * this.headWidthFactor;
            let neckWidth: number = headHeight * this.neckWidthFactor;
            let neckHeight: number = headHeight * this.neckHeightFactor;
            let headEndPnt: Point = PlotUtils.getThirdPoint(points[points.length - 2], headPnt, 0, headHeight, true);
            let neckEndPnt: Point = PlotUtils.getThirdPoint(points[points.length - 2], headPnt, 0, neckHeight, true);
            let headLeft: Point = PlotUtils.getThirdPoint(headPnt, headEndPnt, Constants.HALF_PI, headWidth, false);
            let headRight: Point = PlotUtils.getThirdPoint(headPnt, headEndPnt, Constants.HALF_PI, headWidth, true);
            let neckLeft: Point = PlotUtils.getThirdPoint(headPnt, neckEndPnt, Constants.HALF_PI, neckWidth, false);
            let neckRight: Point = PlotUtils.getThirdPoint(headPnt, neckEndPnt, Constants.HALF_PI, neckWidth, true);
            return [neckLeft, headLeft, headPnt, headRight, neckRight];
        } catch (e) {
            console.log(e);
        }
    }

    /**
     * 插值面部分数据
     * @param points
     * @param neckLeft
     * @param neckRight
     * @param tailWidthFactor
     * @returns {Array.<*>}
     */
    private getArrowBodyPoints(points: Array<Point>, neckLeft: Point, neckRight: Point, tailWidthFactor: number): Array<Point> {
        let allLen: number = PlotUtils.wholeDistance(points);
        let len: number = PlotUtils.getBaseLength(points);
        let tailWidth: number = len * tailWidthFactor;
        let neckWidth: number = PlotUtils.mathDistance(neckLeft, neckRight);
        let widthDif: number = (tailWidth - neckWidth) / 2;
        let [tempLen, leftBodyPoints, rightBodyPoints]: [tempLen: number, leftBodyPoints: Array<Point>, rightBodyPoints: Array<Point>] = [0, [], []];
        for (let i: number = 1; i < points.length - 1; i++) {
            let angle: number = PlotUtils.getAngleOfThreePoints(points[i - 1], points[i], points[i + 1]) / 2;
            tempLen += PlotUtils.mathDistance(points[i - 1], points[i]);
            let w: number = (tailWidth / 2 - tempLen / allLen * widthDif) / Math.sin(angle);
            let left: Point = PlotUtils.getThirdPoint(points[i - 1], points[i], Math.PI - angle, w, true);
            let right: Point = PlotUtils.getThirdPoint(points[i - 1], points[i], angle, w, false);
            leftBodyPoints.push(left);
            rightBodyPoints.push(right);
        }
        return leftBodyPoints.concat(rightBodyPoints);
    }

    /**
     * 获取对称点
     * @param linePnt1
     * @param linePnt2
     * @param point
     * @returns {*}
     */
    private getTempPoint4(linePnt1: Point, linePnt2: Point, point: Point): Point | undefined {

        try {
            let midPnt: Point = PlotUtils.Mid(linePnt1, linePnt2);
            let len: number = PlotUtils.mathDistance(midPnt, point);
            let angle: number = PlotUtils.getAngleOfThreePoints(linePnt1, midPnt, point);
            let symPnt: Point;
            let distance1: number;
            let distance2: number;
            let mid: Point;
            if (angle < Constants.HALF_PI) {
                distance1 = len * Math.sin(angle);
                distance2 = len * Math.cos(angle);
                mid = PlotUtils.getThirdPoint(linePnt1, midPnt, Constants.HALF_PI, distance1, false);
                symPnt = PlotUtils.getThirdPoint(midPnt, mid, Constants.HALF_PI, distance2, true);
            } else if (angle >= Constants.HALF_PI && angle < Math.PI) {
                distance1 = len * Math.sin(Math.PI - angle);
                distance2 = len * Math.cos(Math.PI - angle);
                mid = PlotUtils.getThirdPoint(linePnt1, midPnt, Constants.HALF_PI, distance1, false);
                symPnt = PlotUtils.getThirdPoint(midPnt, mid, Constants.HALF_PI, distance2, false);
            } else if (angle >= Math.PI && angle < Math.PI * 1.5) {
                distance1 = len * Math.sin(angle - Math.PI);
                distance2 = len * Math.cos(angle - Math.PI);
                mid = PlotUtils.getThirdPoint(linePnt1, midPnt, Constants.HALF_PI, distance1, true);
                symPnt = PlotUtils.getThirdPoint(midPnt, mid, Constants.HALF_PI, distance2, true);
            } else {
                distance1 = len * Math.sin(Math.PI * 2 - angle);
                distance2 = len * Math.cos(Math.PI * 2 - angle);
                mid = PlotUtils.getThirdPoint(linePnt1, midPnt, Constants.HALF_PI, distance1, true);
                symPnt = PlotUtils.getThirdPoint(midPnt, mid, Constants.HALF_PI, distance2, false);
            }
            return symPnt;
        } catch (e) {
            console.log(e);
        }
    }
}
